package handle

import (
	"bufio"
	"context"
	"fmt"
	"log"
	"net/http"
	"os"
	"strings"
	"sync"
	"time"

	"github.com/PuerkitoBio/goquery"
	"github.com/pelletier/go-toml/v2"
)

func NewHandle(i, o string) Handle {
	return Handle{
		Input:  i,
		OutPut: o,
		temp:   NewLibVersion(),
		data:   make(chan string, 5),
	}
}

type Handle struct {
	Input  string `json:"input"`
	OutPut string `json:"output"`
	data   chan string
	temp   LibVersion
}

func (r *Handle) HandleInput() {
	open, err := os.Open(r.Input)
	if err != nil {
		panic(err)
	}
	defer open.Close()

	scanner := bufio.NewScanner(open)
	scanner.Split(bufio.ScanLines)
	for scanner.Scan() {
		space := strings.TrimSpace(scanner.Text())
		if len(space) == 0 {
			continue
		}
		if strings.HasPrefix(space, "//") {
			continue
		}
		r.data <- space
	}
	close(r.data)
}

func handleUpdateDep(dep Dep, c *http.Client) (Dep, error) {
	if c == nil {
		c = http.DefaultClient
	}
	url, err := dep.URL()
	if err != nil {
		log.Println("request URL", err)
		return Dep{}, err
	}
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	request, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
	if err != nil {
		log.Println("request NewRequestWithContext", err)
		return Dep{}, err
	}

	hearder(request.Header)

	do, err := c.Do(request)
	if err != nil {
		log.Println("Do", err)
		return Dep{}, err
	}
	defer do.Body.Close()
	//vbtn release
	reader, err := goquery.NewDocumentFromReader(do.Body)
	if err != nil {
		log.Println("goquery", err)
		return Dep{}, err
	}
	text := reader.Find(".grid.versions .vbtn.release").First().Text()
	version := strings.TrimSpace(text)
	fmt.Println("update! ", dep, version)
	dep.Version = version
	return dep, nil
}

func (r *Handle) handleDep(dep Dep) {
	var vKey Key
	switch dep.IsPlugin() {
	case true:
		key, plugins := dep.ToPlugin()
		r.temp.Plugins[key] = plugins
		vKey = key
	default:
		key, library := dep.ToLib()
		r.temp.Libraries[key] = library
		vKey = key
	}
	r.temp.Versions[dep.Version] = vKey.Key()
	log.Printf("%+v\n", dep)
}

func hearder(header http.Header) {
	header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7")
	header.Set("Sec-Fetch-Dest", "document")
	header.Set("Sec-Fetch-Mode", "navigate")
	header.Set("Sec-Fetch-Site", "none")
	header.Set("Sec-Fetch-User", "?1")
	header.Set("Upgrade-Insecure-Requests", "1")
	header.Set("User-Agent", `Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36`)
	header.Set("sec-ch-ua", `"Not)A;Brand";v="8", "Chromium";v="138", "Google Chrome";v="138"`)
	header.Set("sec-ch-ua-mobile", `?0`)
	header.Set("sec-ch-ua-platform", `"Windows"`)
}

func (r *Handle) HandleOutput() {

	for key, plugin := range r.temp.Plugins {
		fmt.Println("plugin", key, plugin)
		plugin.VersionRef = r.temp.Versions[plugin.VersionRef]
		r.temp.Plugins[key] = plugin
	}
	for key, plugin := range r.temp.Libraries {
		plugin.VersionRef = r.temp.Versions[plugin.VersionRef]
		r.temp.Libraries[key] = plugin
	}

	m := make(map[string]string, len(r.temp.Versions))
	for s, s2 := range r.temp.Versions {
		m[s2] = s
	}
	r.temp.Versions = m
}

func (r *Handle) HandleFormat() {

	file, err := os.OpenFile(r.OutPut, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0644)
	if err != nil {
		panic(err)
	}
	defer file.Close()
	tables := toml.NewEncoder(file).
		SetTablesInline(true)

	file.WriteString("[versions]\n")
	err = tables.Encode(r.temp.Versions)
	if err != nil {
		panic(err)
	}
	file.WriteString("[libraries]\n")
	err = tables.Encode(r.temp.Libraries)
	if err != nil {
		panic(err)
	}
	file.WriteString("[plugins]\n")
	err = tables.Encode(r.temp.Plugins)
	if err != nil {
		panic(err)
	}
	file.WriteString("[bundles]\n")
	err = tables.Encode(r.temp.Bundles)
	if err != nil {
		panic(err)
	}
}

func (r *Handle) Handle(c *http.Client) {
	data := &sync.Map{}
	for datum := range r.data {
		imp := Imp(datum)
		dep, err := imp.ToDep()
		if err != nil {
			panic(err)
		}
		updateDep, err := handleUpdateDep(dep, c)
		if err != nil {
			panic(err)
		}
		r.handleDep(updateDep)
		data.Store(dep.ToRaw(updateDep.Version), 1)
	}
	fmt.Println("data end")
	file, err := os.OpenFile(r.OutPut+".txt", os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0644)
	if err != nil {
		panic(err)
	}
	data.Range(func(key, value any) bool {
		file.WriteString(key.(string) + "\n")
		return true
	})
	file.Close()
	r.HandleOutput()
	r.HandleFormat()
}
